import * as BABYLON from '@babylonjs/core'
import * as GUI from "@babylonjs/gui";
import { AbstractDevice } from "../../AbstractDevice";
import { TWcsSceneDevice3d, TWcsSceneDeviceInfo, TWcsSceneDevice2d } from "../../../models";
import { KrData } from "../../KrData";
import { Drawer } from '../helper/Drawer';
import { Arrow } from './Arrow';
import { String } from 'lodash';
import { Task } from '../../../Task';
import { Label } from './Label';

export class Device2D extends GUI.Container {
    public onPointerHoverObservable: BABYLON.Observable<Device2D> = new BABYLON.Observable<Device2D>();
    private _layer0: GUI.Rectangle;
    private _layer1: Arrow;
    private _layer2: GUI.Image;
    private _layer3: Label;
    private _layer4?: GUI.Image;
    private _isHover: boolean = false;
    private _hoverTime: number = 0;

    constructor(data: TWcsSceneDevice2d) {
        super(data.controlId)
        this.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
        this.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP;

        this.x = data.x;
        this.y = data.y;
        this.w = data.lengthX;
        this.h = data.lengthY;
        this.scaleX = data.scaleX;
        this.scaleY = data.scaleY;

        this._layer0 = new GUI.Rectangle(data.controlId + '_background');
        this._layer0.background = data.backgroundColor || 'transparent';
        this._layer0.thickness = data.borderSize;
        this._layer0.color = data.borderColor || 'transparent';
        this.addControl(this._layer0);

        this._layer1 = new Arrow(data.controlId + '_arrow');
        this._layer1.color = 'white';
        this.addControl(this._layer1);

        this._layer2 = new GUI.Image(data.controlId + '_image', data.backgroundImage);
        this._layer2.stretch = GUI.Image.STRETCH_UNIFORM;
        this.addControl(this._layer2);

        this._layer3 = new Label(data.controlId + '_textBlock', data.text || '');
        this._layer3.fontFamily = data.font || 'arial';
        this._layer3.fontSize = data.fontSize;
        this._layer3.color = data.fontColor || '#333';
        if(data.textVAlignment) {
            this._layer3.verticalAlignment = data.textVAlignment;
        }
        if(data.textHAlignment) {
            this._layer3.horizontalAlignment = data.textHAlignment;
        }
        this.addControl(this._layer3);

        if(data.showFormType) {
            this.hoverCursor = 'pointer';
            this.isPointerBlocker = true;
        }

        this.onPointerEnterObservable.add(this.onPointEnter);
        this.onPointerOutObservable.add(this.onPointOut);
        this.onPointerMoveObservable.add(this.onPointMove);
    }
    
    public get center() {
        return new BABYLON.Vector2(this.centerX, this.centerY);
    }

    public get getWordRectangle() {
        var x = this.transformedMeasure.left;
        var y = this.transformedMeasure.top;
        var w = this.transformedMeasure.width;
        var h = this.transformedMeasure.height;
        // var current = this as GUI.Container;
        // while(current.parent) {
        //     x += current.transformedMeasure.left;
        //     y += current.transformedMeasure.top;

        //     current = current.parent;
        // }

        // x += current._currentMeasure.width - current._currentMeasure.width * sx;
        // y += current._currentMeasure.height - current._currentMeasure.height * sy;
        return {x,y,w,h};
    }

    public get x() {
        return this.leftInPixels;
    }
    public set x(value: number) {
        this.left = value + 'px';
    }

    public get y() {
        return this.topInPixels;
    }
    public set y(value: number) {
        this.top = value + 'px';
    }
    public get z() {
        return this.zIndex;
    }
    public set z(value: number) {
        this.zIndex = value;
    }
    public get w() {
        return this.widthInPixels;
    }
    public set w(value: number) {
        this.width = value + 'px';
    }
    public get h() {
        return this.heightInPixels;
    }
    public set h(value: number) {
        this.height = value + 'px';
    }
    public get text() {
        return this._layer3.text;
    }
    public set text(value: string| undefined) {
        this._layer3.text = value || '';
    }
    public get backgroundColor(): string {
        return this._layer0.background;
    }
    public set backgroundColor(value: string) {
        this._layer0.background = value
    }
    public get border() {
        return this._layer0.thickness;
    }
    public set border(value: number) {
        this._layer0.thickness = value;
    }
    public get borderColor(): string | undefined {
        return this._layer0?.color;
    }
    public set borderColor(value: string) {
        this._layer0.color = value;
    }
    public get backgroundImage(): GUI.Image | undefined {
        return this._layer2;
    }
    public set backgroundImage(url: string | undefined) {
        this._layer2 = new GUI.Image(this.name + '_image', url);
        this._layer2.stretch = GUI.Image.STRETCH_UNIFORM;
    }

    public get direction(): string | undefined {
        return this._layer1.direction;
    }
    public set direction(value: string | undefined) {
        this._layer1.direction = value?.toUpperCase();
    }

    public update(info: KrData, colors: TWcsSceneDeviceInfo[]) {
        for(var color of colors) {
            if(this.evalInContext(info, color.infoExpression)) {
                this.backgroundColor = color.infoColor || 'transparent';
                return;
            }
        }
    }

    /**
     * 在指定对象的上下文中执行表达式
     * @param {any} o - 上下文对象，将被绑定到$变量
     * @param {string | undefined} expression - 要执行的表达式
     * @returns {any} 表达式的计算结果
     */
    private evalInContext(o: any, expression: string | undefined) {
        if(!expression) {
            return o;
        }
      try {
        // 创建一个函数，将对象o绑定到$变量，并执行表达式
        const evaluator = new Function('$', `return ${expression};`);
        return evaluator(o);
      } catch (error) {
        console.error('表达式执行错误:', error);
        return undefined;
      }
    }

    private static hovers: Map<string, boolean> = new Map();
    private onPointEnter(eventData: GUI.Control, eventState: BABYLON.EventState) {
        Device2D.hovers.set(this.name!, true);
        this._hoverTime = Date.now();
        const hoverDelay = 1000;
        const checkHover = () => {
            const isHover = Device2D.hovers.get(this.name!);
            const next = this._hoverTime + hoverDelay;
            if(isHover && Date.now() >= next) {
                Device2D.hovers.set(this.name!, false);
                this.onPointerHoverObservable?.notifyObservers(this);
            } else {
                if(isHover) {
                    Task.delay(next - Date.now()).then(checkHover);
                }
            }
        }
        Task.delay(hoverDelay).then(checkHover);
    }

    private onPointOut(eventData: GUI.Control, eventState: BABYLON.EventState) {
        Device2D.hovers.set(this.name!, false);
    }

    private onPointMove(eventData: BABYLON.Vector2, eventState: BABYLON.EventState) {
        this._hoverTime = Date.now();
    }
}